From fb8b2182bc6b1beb7f431257449ada4781c553b3 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Fri, 28 Mar 2008 09:50:50 +0000 Subject: [PATCH] ioemu: Perform emulated IDE flushes asynchronously. Fixes 'Windows Bug Check 0x101 issue' in which a VCPU gets tied up for so long doing a synchronous flush to disc that it misses critical timer events. Signed-off-by: Ian Jackson Modified-by: Ian Jackson Signed-off-by: Kouya Shimura --- tools/ioemu/block-qcow.c | 8 ++++++ tools/ioemu/block-qcow2.c | 8 ++++++ tools/ioemu/block-raw.c | 17 +++++++++++++ tools/ioemu/block.c | 23 +++++++++++++++++ tools/ioemu/block_int.h | 2 ++ tools/ioemu/hw/ide.c | 52 ++++++++++++++++++++++++++++++++++----- tools/ioemu/vl.h | 2 ++ 7 files changed, 106 insertions(+), 6 deletions(-) diff --git a/tools/ioemu/block-qcow.c b/tools/ioemu/block-qcow.c index 451d28639c..e59b600f1d 100644 --- a/tools/ioemu/block-qcow.c +++ b/tools/ioemu/block-qcow.c @@ -783,6 +783,13 @@ static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) qemu_aio_release(acb); } +static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVQcowState *s = bs->opaque; + return bdrv_aio_flush(s->hd, cb, opaque); +} + static void qcow_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -957,6 +964,7 @@ BlockDriver bdrv_qcow = { .bdrv_aio_read = qcow_aio_read, .bdrv_aio_write = qcow_aio_write, .bdrv_aio_cancel = qcow_aio_cancel, + .bdrv_aio_flush = qcow_aio_flush, .aiocb_size = sizeof(QCowAIOCB), .bdrv_write_compressed = qcow_write_compressed, .bdrv_get_info = qcow_get_info, diff --git a/tools/ioemu/block-qcow2.c b/tools/ioemu/block-qcow2.c index 64ec49034a..ec64448a34 100644 --- a/tools/ioemu/block-qcow2.c +++ b/tools/ioemu/block-qcow2.c @@ -1007,6 +1007,13 @@ static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) qemu_aio_release(acb); } +static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVQcowState *s = bs->opaque; + return bdrv_aio_flush(s->hd, cb, opaque); +} + static void qcow_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -2241,6 +2248,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_aio_read = qcow_aio_read, .bdrv_aio_write = qcow_aio_write, .bdrv_aio_cancel = qcow_aio_cancel, + .bdrv_aio_flush = qcow_aio_flush, .aiocb_size = sizeof(QCowAIOCB), .bdrv_write_compressed = qcow_write_compressed, diff --git a/tools/ioemu/block-raw.c b/tools/ioemu/block-raw.c index 182d2ec55e..7584aa38a0 100644 --- a/tools/ioemu/block-raw.c +++ b/tools/ioemu/block-raw.c @@ -496,6 +496,21 @@ static void raw_aio_cancel(BlockDriverAIOCB *blockacb) pacb = &acb->next; } } + +static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + RawAIOCB *acb; + + acb = raw_aio_setup(bs, 0, NULL, 0, cb, opaque); + if (!acb) + return NULL; + if (aio_fsync(O_SYNC, &acb->aiocb) < 0) { + qemu_aio_release(acb); + return NULL; + } + return &acb->common; +} #endif static void raw_close(BlockDriverState *bs) @@ -621,6 +636,7 @@ BlockDriver bdrv_raw = { .bdrv_aio_read = raw_aio_read, .bdrv_aio_write = raw_aio_write, .bdrv_aio_cancel = raw_aio_cancel, + .bdrv_aio_flush = raw_aio_flush, .aiocb_size = sizeof(RawAIOCB), #endif .protocol_name = "file", @@ -959,6 +975,7 @@ BlockDriver bdrv_host_device = { .bdrv_aio_read = raw_aio_read, .bdrv_aio_write = raw_aio_write, .bdrv_aio_cancel = raw_aio_cancel, + .bdrv_aio_flush = raw_aio_flush, .aiocb_size = sizeof(RawAIOCB), #endif .bdrv_pread = raw_pread, diff --git a/tools/ioemu/block.c b/tools/ioemu/block.c index 6e8f8cc737..3e25ccb544 100644 --- a/tools/ioemu/block.c +++ b/tools/ioemu/block.c @@ -48,6 +48,8 @@ static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb); +static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque); static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, @@ -155,6 +157,8 @@ void bdrv_register(BlockDriver *bdrv) bdrv->bdrv_read = bdrv_read_em; bdrv->bdrv_write = bdrv_write_em; } + if (!bdrv->bdrv_aio_flush) + bdrv->bdrv_aio_flush = bdrv_aio_flush_em; bdrv->next = first_drv; first_drv = bdrv; } @@ -1138,6 +1142,17 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb) drv->bdrv_aio_cancel(acb); } +BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return NULL; + + return drv->bdrv_aio_flush(bs, cb, opaque); +} + /**************************************************************/ /* async block device emulation */ @@ -1214,6 +1229,14 @@ static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb) } #endif /* !QEMU_TOOL */ +static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + bdrv_flush(bs); + cb(opaque, 0); + return NULL; +} + /**************************************************************/ /* sync block device emulation */ diff --git a/tools/ioemu/block_int.h b/tools/ioemu/block_int.h index c7a9d197b3..6190d1c08e 100644 --- a/tools/ioemu/block_int.h +++ b/tools/ioemu/block_int.h @@ -49,6 +49,8 @@ struct BlockDriver { int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); void (*bdrv_aio_cancel)(BlockDriverAIOCB *acb); + BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque); int aiocb_size; const char *protocol_name; diff --git a/tools/ioemu/hw/ide.c b/tools/ioemu/hw/ide.c index 5acefce44b..0db6f2a6b3 100644 --- a/tools/ioemu/hw/ide.c +++ b/tools/ioemu/hw/ide.c @@ -751,6 +751,7 @@ static inline void ide_abort_command(IDEState *s) static inline void ide_set_irq(IDEState *s) { BMDMAState *bm = s->bmdma; + if (!s->bs) return; /* yikes */ if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) { if (bm) { bm->status |= BM_STATUS_INT; @@ -916,6 +917,8 @@ static void ide_read_dma_cb(void *opaque, int ret) int n; int64_t sector_num; + if (!s->bs) return; /* yikes */ + n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { @@ -1024,6 +1027,8 @@ static void ide_write_dma_cb(void *opaque, int ret) int n; int64_t sector_num; + if (!s->bs) return; /* yikes */ + n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { @@ -1072,6 +1077,39 @@ static void ide_sector_write_dma(IDEState *s) ide_dma_start(s, ide_write_dma_cb); } +static void ide_device_utterly_broken(IDEState *s) { + s->status |= BUSY_STAT; + s->bs = NULL; + /* This prevents all future commands from working. All of the + * asynchronous callbacks (and ide_set_irq, as a safety measure) + * check to see whether this has happened and bail if so. + */ +} + +static void ide_flush_cb(void *opaque, int ret) +{ + IDEState *s = opaque; + + if (!s->bs) return; /* yikes */ + + if (ret) { + /* We are completely doomed. The IDE spec does not permit us + * to return an error from a flush except via a protocol which + * requires us to say where the error is and which + * contemplates the guest repeating the flush attempt to + * attempt flush the remaining data. We can't support that + * because f(data)sync (which is what the block drivers use + * eventually) doesn't report the necessary information or + * give us the necessary control. So we make the disk vanish. + */ + ide_device_utterly_broken(s); + return; + } + else + s->status = READY_STAT; + ide_set_irq(s); +} + static void ide_atapi_cmd_ok(IDEState *s) { s->error = 0; @@ -1298,6 +1336,8 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) IDEState *s = bm->ide_if; int data_offset, n; + if (!s->bs) return; /* yikes */ + if (ret < 0) { ide_atapi_io_error(s, ret); goto eot; @@ -1703,6 +1743,8 @@ static void cdrom_change_cb(void *opaque) IDEState *s = opaque; int64_t nb_sectors; + if (!s->bs) return; /* yikes */ + /* XXX: send interrupt too */ bdrv_get_geometry(s->bs, &nb_sectors); s->nb_sectors = nb_sectors; @@ -1806,8 +1848,8 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) printf("ide: CMD=%02x\n", val); #endif s = ide_if->cur_drive; - /* ignore commands to non existant slave */ - if (s != ide_if && !s->bs) + /* ignore commands to non existant device */ + if (!s->bs) break; switch(val) { @@ -1976,10 +2018,8 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case WIN_FLUSH_CACHE: case WIN_FLUSH_CACHE_EXT: - if (s->bs) - bdrv_flush(s->bs); - s->status = READY_STAT; - ide_set_irq(s); + s->status = BUSY_STAT; + bdrv_aio_flush(s->bs, ide_flush_cb, s); break; case WIN_IDLEIMMEDIATE: case WIN_STANDBY: diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h index 0a90fcc29e..79531bf783 100644 --- a/tools/ioemu/vl.h +++ b/tools/ioemu/vl.h @@ -653,6 +653,8 @@ BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); void bdrv_aio_cancel(BlockDriverAIOCB *acb); +BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque); void qemu_aio_init(void); void qemu_aio_poll(void); -- 2.30.2